﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Security.Cryptography;
using System.Web;
using System.Net;
using System.IO;
using System.Xml;
using System.Threading;

namespace RiotTweet
{
    public class TwitterAccess
    {
        protected const string URL_REQUEST_TOKEN = "https://api.twitter.com/oauth/request_token";
        protected const string URL_AUTHORIZE = "https://twitter.com/oauth/authorize";
        protected const string URL_ACCESS_TOKEN = "https://api.twitter.com/oauth/access_token";

        protected const string METHOD_GET = "GET";
        protected const string METHOD_POST = "POST";

        public class StatusInfo
        {
            public string screen_name { get; set; }
            public string text { get; set; }
            public DateTime created_at { get; set; }
            public long id { get; set; }
            public long in_reply_to_status_id { get; set; }
            public long user_id { get; set; }
            public string name { get; set; }
            public string profile_image_url { get; set; }
            public string url { get; set; }
            public Dictionary<string, string> attributes;
            public bool retweeted;
            public bool retweeted_status;
            public long retweeted_status_id;
        }

        public class UserInfo
        {
            public long id { get; set; }
            public string name { get; set; }
            public string screen_name { get; set; }
            public string url { get; set; }
        }

        public class TwitterException : WebException
        {
            public TwitterException(string message)
                : base(message)
            {
            }
            public TwitterException(string message, Exception innerException)
                : base(message, innerException)
            {
            }
        }

        public string ConsumerKey { get; set; }
        public string ConsumerSecret { get; set; }

        public string LoginOauthToken { get; set; }
        public string OauthToken { get; set; }
        public string OauthTokenSecret { get; set; }

#if DEBUG
        public string DebugNonce { get; set; }
        public string DebugTimestamp { get; set; }
#endif

        public UserInfo CurrentUser { get; protected set; }

        protected Dictionary<string, string> MakeParams()
        {
            string nonce = (new Random().Next(999999999) + 1000000000).ToString();
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            string timestamp = Convert.ToInt64(ts.TotalSeconds).ToString();

            Dictionary<string, string> requestParams = new Dictionary<string, string>();
            requestParams.Add("oauth_consumer_key", ConsumerKey);
            requestParams.Add("oauth_nonce", nonce);
            requestParams.Add("oauth_signature_method", "HMAC-SHA1");
            requestParams.Add("oauth_timestamp", timestamp);
            requestParams.Add("oauth_version", "1.0");

            if (OauthToken != null)
            {
                requestParams.Add("oauth_token", OauthToken);
            }

#if DEBUG
            if (DebugNonce != null)
            {
                requestParams["oauth_nonce"] = DebugNonce;
            }
            if (DebugNonce != null)
            {
                requestParams["oauth_timestamp"] = DebugTimestamp;
            }
#endif
            return requestParams;
        }

        protected string[] GetKeysSorted(ICollection<string> keys)
        {
            string[] bufs = new string[keys.Count];
            int index = 0;
            foreach (var item in keys)
            {
                bufs[index++] = item;
            }
            Array.Sort(bufs);
            return bufs;
        }

        protected string MakeUrl(string method, string url, Dictionary<string, string> requestParams)
        {
            string[] paramKeys = GetKeysSorted(requestParams.Keys);

            List<string> requestParamList = new List<string>();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + requestParams[key]);
            }

            string sigurl = String.Join("&", requestParamList.ToArray());
            sigurl = UrlEncodeUpper(sigurl);

            sigurl = method + "&" + UrlEncodeUpper(url) + "&" + sigurl;

            HMACSHA1 hmacsha1 = new HMACSHA1();
            hmacsha1.Key = Encoding.ASCII.GetBytes(ConsumerSecret + "&" + OauthTokenSecret);
            byte[] hash = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(sigurl));
            string sighash = Convert.ToBase64String(hash);

            requestParams.Add("oauth_signature", sighash);

            paramKeys = GetKeysSorted(requestParams.Keys);

            requestParamList.Clear();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + UrlEncodeUpper(requestParams[key]));
            }

            string result = url + '?' + String.Join("&", requestParamList.ToArray());

            System.Diagnostics.Debug.WriteLine("sig: " + sigurl);
            System.Diagnostics.Debug.WriteLine("url: " + result);

            return result;
        }

        protected void MakeUrl2(string method, string url, Dictionary<string, string> requestParams, out string resultUrl, out string resultAuth)
        {
            string[] paramKeys = GetKeysSorted(requestParams.Keys);

            List<string> requestParamList = new List<string>();
            List<string> oauthParamList = new List<string>();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + requestParams[key]);
                if (key.StartsWith("oauth_"))
                {
                    oauthParamList.Add(key);
                }
            }

            string sigurl = String.Join("&", oauthParamList.ToArray());
            sigurl = UrlEncodeUpper(sigurl);

            sigurl = method + "&" + UrlEncodeUpper(url) + "&" + sigurl;

            HMACSHA1 hmacsha1 = new HMACSHA1();
            hmacsha1.Key = Encoding.ASCII.GetBytes(ConsumerSecret + "&" + OauthTokenSecret);
            byte[] hash = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(sigurl));
            string sighash = Convert.ToBase64String(hash);

            requestParams.Add("oauth_signature", sighash);

            paramKeys = GetKeysSorted(requestParams.Keys);

            requestParamList.Clear();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + UrlEncodeUpper(requestParams[key]));
            }

            //            string result = url + '?' + String.Join("&", requestParamList.ToArray());
            string result = url;
            if (method != METHOD_POST)
            {
                result += '?' + String.Join("&", requestParamList.ToArray());
            }

            //System.Diagnostics.Debug.WriteLine("sig: " + sigurl);
            //System.Diagnostics.Debug.WriteLine("url: " + result);

            resultUrl = result;

            resultAuth = MakeAuth2(requestParams);

        }

        protected static string UrlEncodeUpper(string value)
        {
            StringBuilder buf = new StringBuilder();
            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(value);

            foreach (var b in bytes)
            {
                if ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9'))
                {
                    buf.Append((char)b);
                }
                else if ((b == '-') || (b == '.') || (b == '_') || (b == '~'))
                {
                    buf.Append((char)b);
                }
                else
                {
                    buf.Append(String.Format("%{0,2:X}", (uint)b));
                }
            }
            return buf.ToString();
        }

        //protected HttpWebResponse PostMessage(string method, string url)
        //{
        //    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
        //    request.Method = method;
        //    request.Headers.Add("Authorization", "OAuth");

        //    try
        //    {
        //        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        //        return response;
        //    }
        //    catch (WebException we)
        //    {
        //        HttpWebResponse res = (HttpWebResponse)we.Response;
        //        string result = new StreamReader(res.GetResponseStream()).ReadToEnd();
        //        ErrorMessage = result;
        //        throw we;
        //    }
        //}

        protected delegate void SendRequestParse(HttpWebResponse response, Dictionary<string, object> results);

        protected void SendRequest(string method, string url, Dictionary<string, string> requestParams, SendRequestParse parseRequest, Dictionary<string, object> results)
        {
            Dictionary<string, string> defaultRequestParams = MakeParams();

            foreach (var key in defaultRequestParams.Keys)
            {
                if (!requestParams.ContainsKey(key))
                {
                    requestParams.Add(key, defaultRequestParams[key]);
                }
            }
            string[] paramKeys = GetKeysSorted(requestParams.Keys);

            List<string> requestParamList = new List<string>();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + UrlEncodeUpper(requestParams[key]));
            }

            string sigparam = String.Join("&", requestParamList.ToArray());
            string sigurl = method + "&" + UrlEncodeUpper(url) + "&" + UrlEncodeUpper(sigparam);

            string authSecret = "";
            if (requestParams["oauth_token"] != "")
            {
                authSecret = OauthTokenSecret;
            }
            HMACSHA1 hmacsha1 = new HMACSHA1();
            hmacsha1.Key = Encoding.ASCII.GetBytes(ConsumerSecret + "&" + authSecret);
            byte[] hash = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(sigurl));
            string sighash = Convert.ToBase64String(hash);

            requestParams.Add("oauth_signature", sighash);

            paramKeys = GetKeysSorted(requestParams.Keys);

            requestParamList.Clear();
            List<string> tmplist = new List<string>();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + UrlEncodeUpper(requestParams[key]));
                if (key.StartsWith("oauth_"))
                {
                    tmplist.Add(key + "=\"" + UrlEncodeUpper(requestParams[key]) + "\"");
                }
            }

            string resultUrl = url;
            if (method != METHOD_POST)
            {
                resultUrl += '?' + String.Join("&", requestParamList.ToArray());
            }

            string resultAuth = "OAuth " + String.Join(",", tmplist.ToArray());

            string post = null;
            if (method == METHOD_POST)
            {
                post = "";
                foreach (var key in paramKeys)
                {
                    if (!key.StartsWith("oauth_"))
                    {
                        if (post.Length > 0)
                        {
                            post += "&";
                        }
                        post += key + "=" + UrlEncodeUpper(requestParams[key]);
                    }
                }
            }

            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(resultUrl);

            request.Headers.Add(HttpRequestHeader.Authorization, resultAuth);
            request.Method = method;

            if (method == METHOD_POST)
            {
                using (var s = new StreamWriter(request.GetRequestStream()))
                {
                    s.Write(post);
                }
            }

            using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
            {
                if (parseRequest != null)
                {
                    parseRequest(response, results);
                }
            }
        }

        protected void PostOauthRequestTokenParse(HttpWebResponse res, Dictionary<string, object> results)
        {
            string[] tokens = new StreamReader(res.GetResponseStream()).ReadToEnd().Split('&');

            string oauth_token = tokens[0].Replace("oauth_token=", "");
            LoginOauthToken = oauth_token;

            string authUrl = URL_AUTHORIZE + "?oauth_token=" + oauth_token;
            results.Add("authUrl", authUrl);
            //return URL_AUTHORIZE + "?oauth_token=" + oauth_token;
        }

        public string PostOauthRequestToken()
        {
            //string url = CreateRequestTokenUrl();

            //HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            //request.Method = "POST";

            const string METHOD = METHOD_POST;
            const string URL = URL_REQUEST_TOKEN;

            //Dictionary<string, string> requestParams = new Dictionary<string, string>();

            //TwitterRequestData requestData = CreateRequestData(METHOD, URL, requestParams);

            //HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(requestData.url);
            //request.Headers.Add(HttpRequestHeader.Authorization, requestData.auth);
            //request.Method = requestData.method;

            //if (request.Method == METHOD_POST)
            //{
            //    var s = new StreamWriter(request.GetRequestStream());
            //    s.Write(requestData.post);
            //    s.Close();
            //}

            //try
            //{
            //    HttpWebResponse res = (HttpWebResponse)request.GetResponse();
            //    string[] tokens = new StreamReader(res.GetResponseStream()).ReadToEnd().Split('&');

            //    string oauth_token = tokens[0].Replace("oauth_token=", "");
            //    LoginOauthToken = oauth_token;
            //    return URL_AUTHORIZE + "?oauth_token=" + oauth_token;
            //}
            //catch (WebException we)
            //{
            //    HttpWebResponse res = (HttpWebResponse)we.Response;
            //    string result = new StreamReader(res.GetResponseStream()).ReadToEnd();
            //    ErrorMessage = result;
            //    throw we;
            //}

            Dictionary<string, string> requestParams = new Dictionary<string, string>();
            requestParams.Add("oauth_token", ""); 
            Dictionary<string, object> results = new Dictionary<string, object>();

            try
            {
                SendRequest(METHOD, URL, requestParams, PostOauthRequestTokenParse, results);

                string authUrl = (string)results["authUrl"];
                return authUrl;
            }
            catch (WebException we)
            {
                throw new TwitterException("認証トークンの取得に失敗しました。", we);
            }
        }

        public string ErrorMessage { get; set; }

        //protected string CreateAccessTokenUrl(string pin)
        //{
        //    Dictionary<string, string> requestParams = MakeParams();
        //    requestParams.Add("oauth_verifier", pin);
        //    requestParams.Add("oauth_token", LoginOauthToken);

        //    string url = MakeUrl("POST", URL_ACCESS_TOKEN, requestParams);

        //    return url;
        //}

        protected void PostOauthAccessTokenParse(HttpWebResponse res, Dictionary<string, object> results)
        {
            string result = new StreamReader(res.GetResponseStream()).ReadToEnd();
            string[] tokens = result.Split('&');

            string oauth_token = tokens[0].Split('=')[1];
            string oauth_token_secret = tokens[1].Split('=')[1];

            OauthToken = oauth_token;
            OauthTokenSecret = oauth_token_secret;

            results["oauth_token_secret"] = oauth_token_secret;
            //return oauth_token_secret;
        }

        public string PostOauthAccessToken(string pin)
        {
            const string METHOD = METHOD_POST;
            const string URL = URL_ACCESS_TOKEN;

            Dictionary<string, string> requestParams = new Dictionary<string, string>();
            requestParams.Add("oauth_verifier", pin);
            requestParams.Add("oauth_token", LoginOauthToken);

            Dictionary<string, object> results = new Dictionary<string, object>();

            try
            {
                SendRequest(METHOD, URL, requestParams, PostOauthAccessTokenParse, results);

                string oauth_token_secret = (string)results["oauth_token_secret"];
                return oauth_token_secret;
            }
            catch (WebException we)
            {
                throw new TwitterException("ログイン時にエラーが発生しました。", we);
            }


            //string url = CreateAccessTokenUrl(pin);
            //HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            //request.Method = "POST";
            //request.Headers.Add("Authorization", "OAuth");

            //HttpWebResponse res3 = (HttpWebResponse)request.GetResponse();

            //string result = new StreamReader(res3.GetResponseStream()).ReadToEnd();
            //string[] tokens = result.Split('&');

            //string oauth_token = tokens[0].Split('=')[1];
            //string oauth_token_secret = tokens[1].Split('=')[1];

            //OauthToken = oauth_token;
            //OauthTokenSecret = oauth_token_secret;

            //return oauth_token_secret;
        }

        protected DateTime ParseDate(string created_at)
        {
            created_at = created_at.Substring(4);
            string month = created_at.Substring(0, 3);
            if (month == "Jan")
            {
                created_at = created_at.Replace("Jan", "1");
            }
            else if (month == "Feb")
            {
                created_at = created_at.Replace("Feb", "2");
            }
            else if (month == "Mar")
            {
                created_at = created_at.Replace("Mar", "3");
            }
            else if (month == "Apr")
            {
                created_at = created_at.Replace("Apr", "4");
            }
            else if (month == "May")
            {
                created_at = created_at.Replace("May", "5");
            }
            else if (month == "Jun")
            {
                created_at = created_at.Replace("Jun", "6");
            }
            else if (month == "Jul")
            {
                created_at = created_at.Replace("Jul", "7");
            }
            else if (month == "Aug")
            {
                created_at = created_at.Replace("Aug", "8");
            }
            else if (month == "Oct")
            {
                created_at = created_at.Replace("Oct", "9");
            }
            else if (month == "Sep")
            {
                created_at = created_at.Replace("Sep", "10");
            }
            else if (month == "Nov")
            {
                created_at = created_at.Replace("Nov", "11");
            }
            else if (month == "Dec")
            {
                created_at = created_at.Replace("Dec", "12");
            }
            try
            {
                DateTime createdDate = DateTime.ParseExact(created_at, "M dd HH:mm:ss K yyyy", null);
                return createdDate;
            }
            catch (FormatException fe)
            {
                System.Diagnostics.Debug.Assert(false);
                System.Diagnostics.Trace.WriteLine(fe);
                return DateTime.MinValue;
            }
        }

        protected void GetStatusesHomeTimelineParse(HttpWebResponse response, Dictionary<string, object> results)
        {
            List<StatusInfo> list = new List<StatusInfo>();
            XmlDocument doc = new XmlDocument();
            doc.Load(response.GetResponseStream());
            XmlNodeList nodes = doc.SelectNodes("//statuses/status");

            foreach (XmlNode node in nodes)
            {
                Dictionary<string, string> attr = new Dictionary<string, string>();
                //System.Diagnostics.Debug.WriteLine("");
                foreach (XmlNode node2 in node)
                {
                    attr.Add(node2.Name, node2.InnerText);
                    //System.Diagnostics.Debug.WriteLine(node2.Name + "="+ node2.InnerText);
                }
                var userNode = node["user"];
                var retweetedStatusNode = node["retweeted_status"];
                if (retweetedStatusNode != null)
                {
                    foreach (XmlNode node2 in node)
                    {
                        attr.Add("retweeted_status." + node2.Name, node2.InnerText);
                        //System.Diagnostics.Debug.WriteLine("retweeted_status." + node2.Name + "=" + node2.InnerText);
                    }
                }

                string user = userNode["name"].InnerText;
                string screen_name = userNode["screen_name"].InnerText;
                string user_id = userNode["id"].InnerText;
                string iconUrl = userNode["profile_image_url"].InnerText;
                string user_url = userNode["url"].InnerText;

                string id = node["id"].InnerText;
                string retweeted = node["retweeted"].InnerText;
                string in_reply_to_status_id = node["in_reply_to_status_id"].InnerText;

                string message = node["text"].InnerText;
                XmlNode textNode = node["text"].FirstChild;
                if (textNode != null)
                {
                    message = textNode.InnerText;
                    message = message.Replace("&lt;", "<");
                    message = message.Replace("&gt;", ">");
                    message = message.Replace("&quote;", "\"");
                    message = message.Replace("&amp;", "&");
                }
                string created_at = node["created_at"].InnerText;

                DateTime createdDate = ParseDate(created_at);

                StatusInfo item = new StatusInfo();
                item.text = message;
                item.name = user;
                item.screen_name = screen_name;
                item.created_at = createdDate;
                item.id = long.Parse(id);
                item.profile_image_url = iconUrl;
                item.user_id = long.Parse(user_id);
                item.url = user_url;
                item.attributes = attr;
                item.retweeted = (retweeted == "true");
                if (in_reply_to_status_id != "")
                {
                    item.in_reply_to_status_id = long.Parse(in_reply_to_status_id);
                }
                if (attr.ContainsKey("retweeted_status.id"))
                {
                    item.retweeted_status = true;
                    item.retweeted_status_id = long.Parse(attr["retweeted_status.id"]);
                }
                list.Add(item);
            }

            results["list"] = list;
        }

        public List<StatusInfo> GetStatusesHomeTimeline(int count)
        {
            const string METHOD = METHOD_GET;
            const string URL = "https://api.twitter.com/1/statuses/home_timeline.xml";

            Dictionary<string, string> requestParams = new Dictionary<string, string>();
            if (count > 0)
            {
                requestParams.Add("count", count.ToString());
            }

            Dictionary<string, object> results = new Dictionary<string, object>();

            try
            {
                SendRequest(METHOD, URL, requestParams, GetStatusesHomeTimelineParse, results);

                List<StatusInfo> list = (List<StatusInfo>)results["list"];
                return list;
            }
            catch (WebException we)
            {
                throw new TwitterException("タイムラインの取得に失敗しました。", we);
            }

        }

        protected void GetDirectMessagesParse(HttpWebResponse response, Dictionary<string, object> results)
        {
            List<StatusInfo> list = new List<StatusInfo>();
            XmlDocument doc = new XmlDocument();
            doc.Load(response.GetResponseStream());
            XmlNodeList nodes = doc.SelectNodes("//statuses/status");

            foreach (XmlNode node in nodes)
            {
                Dictionary<string, string> attr = new Dictionary<string, string>();
                //System.Diagnostics.Debug.WriteLine("");
                foreach (XmlNode node2 in node)
                {
                    attr.Add(node2.Name, node2.InnerText);
                    //System.Diagnostics.Debug.WriteLine(node2.Name + "="+ node2.InnerText);
                }
                var userNode = node["user"];
                var retweetedStatusNode = node["retweeted_status"];
                if (retweetedStatusNode != null)
                {
                    foreach (XmlNode node2 in node)
                    {
                        attr.Add("retweeted_status." + node2.Name, node2.InnerText);
                        //System.Diagnostics.Debug.WriteLine("retweeted_status." + node2.Name + "=" + node2.InnerText);
                    }
                }

                string user = userNode["name"].InnerText;
                string screen_name = userNode["screen_name"].InnerText;
                string user_id = userNode["id"].InnerText;
                string iconUrl = userNode["profile_image_url"].InnerText;
                string user_url = userNode["url"].InnerText;

                string id = node["id"].InnerText;
                string retweeted = node["retweeted"].InnerText;
                string in_reply_to_status_id = node["in_reply_to_status_id"].InnerText;

                string message = node["text"].InnerText;
                XmlNode textNode = node["text"].FirstChild;
                if (textNode != null)
                {
                    message = textNode.InnerText;
                    message = message.Replace("&lt;", "<");
                    message = message.Replace("&gt;", ">");
                    message = message.Replace("&quote;", "\"");
                    message = message.Replace("&amp;", "&");
                }
                string created_at = node["created_at"].InnerText;

                DateTime createdDate = ParseDate(created_at);

                StatusInfo item = new StatusInfo();
                item.text = message;
                item.name = user;
                item.screen_name = screen_name;
                item.created_at = createdDate;
                item.id = long.Parse(id);
                item.profile_image_url = iconUrl;
                item.user_id = long.Parse(user_id);
                item.url = user_url;
                item.attributes = attr;
                item.retweeted = (retweeted == "true");
                if (in_reply_to_status_id != "")
                {
                    item.in_reply_to_status_id = long.Parse(in_reply_to_status_id);
                }
                if (attr.ContainsKey("retweeted_status.id"))
                {
                    item.retweeted_status = true;
                    item.retweeted_status_id = long.Parse(attr["retweeted_status.id"]);
                }
                list.Add(item);
            }

            results["list"] = list;
        }

        public List<StatusInfo> GetDirectMessages(int count)
        {
            const string METHOD = METHOD_GET;
            const string URL = "https://api.twitter.com/1/direct_messages.xml";

            Dictionary<string, string> requestParams = new Dictionary<string, string>();
            if (count > 0)
            {
                requestParams.Add("count", count.ToString());
            }

            Dictionary<string, object> results = new Dictionary<string, object>();

            try
            {
                SendRequest(METHOD, URL, requestParams, GetDirectMessagesParse, results);

                List<StatusInfo> list = (List<StatusInfo>)results["list"];
                return list;
            }
            catch (WebException we)
            {
                throw new TwitterException("DMの取得に失敗しました。", we);
            }
        }

        public List<StatusInfo> GetStatusesMentions(int count)
        {
            const string METHOD = METHOD_GET;
            const string URL = "https://api.twitter.com/1/statuses/mentions.xml";

            Dictionary<string, string> requestParams = new Dictionary<string, string>();
            if (count > 0)
            {
                requestParams.Add("count", count.ToString());
            }

            Dictionary<string, object> results = new Dictionary<string, object>();

            try
            {
                SendRequest(METHOD, URL, requestParams, GetStatusesHomeTimelineParse, results);

                List<StatusInfo> list = (List<StatusInfo>)results["list"];
                return list;
            }
            catch (WebException we)
            {
                throw new TwitterException("メンションの取得に失敗しました。", we);
            }
        }

        public struct TwitterRequestData
        {
            public string method;
            public string url;
            public string auth;
            public string post;
        }

        protected TwitterRequestData CreateRequestData(string method, string url, Dictionary<string, string> requestParams)
        {
            Dictionary<string, string> defaultRequestParams = MakeParams();

            foreach (var key in defaultRequestParams.Keys)
            {
                if (!requestParams.ContainsKey(key))
                {
                    requestParams.Add(key, defaultRequestParams[key]);
                }
            }
            string[] paramKeys = GetKeysSorted(requestParams.Keys);

            List<string> requestParamList = new List<string>();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + UrlEncodeUpper(requestParams[key]));
            }

            string sigparam = String.Join("&", requestParamList.ToArray());
            string sigurl = method + "&" + UrlEncodeUpper(url) + "&" + UrlEncodeUpper(sigparam);

            HMACSHA1 hmacsha1 = new HMACSHA1();
            hmacsha1.Key = Encoding.ASCII.GetBytes(ConsumerSecret + "&" + OauthTokenSecret);
            byte[] hash = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(sigurl));
            string sighash = Convert.ToBase64String(hash);

            requestParams.Add("oauth_signature", sighash);

            paramKeys = GetKeysSorted(requestParams.Keys);

            requestParamList.Clear();
            List<string> tmplist = new List<string>();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + UrlEncodeUpper(requestParams[key]));
                //                    tmplist.Add(key + "=\"" + UrlEncodeUpper(requestParams[key]) + "\"");
                if (key.StartsWith("oauth_"))
                {
                    tmplist.Add(key + "=\"" + UrlEncodeUpper(requestParams[key]) + "\"");
                }
            }

            string resultUrl = url;
            if (method != METHOD_POST)
            {
                resultUrl += '?' + String.Join("&", requestParamList.ToArray());
            }

            //System.Diagnostics.Debug.WriteLine("sig: " + sigurl);
            //System.Diagnostics.Debug.WriteLine("url: " + result);

            //List<string> tmplist = new List<string>();

            //foreach (var key in paramKeys)
            //{
            //    tmplist.Add(key + "=\"" + UrlEncodeUpper(requestParams[key]) + "\"");
            //}

            string resultAuth = "OAuth " + String.Join(",", tmplist.ToArray());

            string post = null;
            if (method == METHOD_POST)
            {
                post = "";
                foreach (var key in paramKeys)
                {
                    if (!key.StartsWith("oauth_"))
                    {
                        if (post.Length > 0)
                        {
                            post += "&";
                        }
                        post += key + "=" + UrlEncodeUpper(requestParams[key]);
                    }
                }
            }

            TwitterRequestData request = new TwitterRequestData();
            request.method = method;
            request.url = resultUrl;
            request.auth = resultAuth;
            request.post = post;
            return request;
        }

        protected HttpWebRequest CreateRequest(string method, string url, Dictionary<string, string> requestParams)
        {
            Dictionary<string, string> defaultRequestParams = MakeParams();

            foreach (var key in defaultRequestParams.Keys)
            {
                if (!requestParams.ContainsKey(key))
                {
                    requestParams.Add(key, defaultRequestParams[key]);
                }
            }
            string[] paramKeys = GetKeysSorted(requestParams.Keys);

            List<string> requestParamList = new List<string>();
            List<string> oauthParamList = new List<string>();
            List<string> oauthKeyList = new List<string>();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + requestParams[key]);
                if (key.StartsWith("oauth_"))
                {
                    oauthKeyList.Add(key);
                    oauthParamList.Add(key + "=" + requestParams[key]);
                }
            }

            string sigurl = String.Join("&", requestParamList.ToArray());
            //if (method == METHOD_POST)
            //{
            //    sigurl = String.Join("&", oauthParamList.ToArray());
            //}
            sigurl = UrlEncodeUpper(sigurl);

            sigurl = method + "&" + UrlEncodeUpper(url) + "&" + sigurl;

            HMACSHA1 hmacsha1 = new HMACSHA1();
            hmacsha1.Key = Encoding.ASCII.GetBytes(ConsumerSecret + "&" + OauthTokenSecret);
            byte[] hash = hmacsha1.ComputeHash(Encoding.ASCII.GetBytes(sigurl));
            string sighash = Convert.ToBase64String(hash);

            requestParams.Add("oauth_signature", sighash);

            paramKeys = GetKeysSorted(requestParams.Keys);

            requestParamList.Clear();
            foreach (var key in paramKeys)
            {
                requestParamList.Add(key + "=" + UrlEncodeUpper(requestParams[key]));
            }

            string resultUrl = url;
            if (method != METHOD_POST)
            {
                resultUrl += '?' + String.Join("&", requestParamList.ToArray());
            }

            //System.Diagnostics.Debug.WriteLine("sig: " + sigurl);
            //System.Diagnostics.Debug.WriteLine("url: " + result);

            //            string resultAuth = MakeAuth2(requestParams);

            List<string> tmplist = new List<string>();
            string[] keys = GetKeysSorted(requestParams.Keys);
            //if (method == METHOD_POST)
            //{
            //    keys = oauthKeyList.ToArray();
            //}
            foreach (var key in keys)
            {
                tmplist.Add(key + "=\"" + UrlEncodeUpper(requestParams[key]) + "\"");
            }

            string resultAuth = "OAuth " + String.Join(",", tmplist.ToArray());

            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(resultUrl);
            request.Headers.Add(HttpRequestHeader.Authorization, resultAuth);
            request.Method = method;

            return request;
        }

        public void PostStatusesUpdate(string message)
        {
            const string METHOD = METHOD_POST;
            const string URL = "https://api.twitter.com/1/statuses/update.xml";

            Dictionary<string, string> requestParams = new Dictionary<string, string>();
            requestParams.Add("status", message);
            requestParams.Add("include_entities", "true");

            Dictionary<string, object> results = new Dictionary<string, object>();

            try
            {
                SendRequest(METHOD, URL, requestParams, null, null);
            }
            catch (WebException we)
            {
                throw new TwitterException("ツイートに失敗しました。", we);
            }


            
            //const string METHOD = METHOD_POST;
            //const string URL = "https://api.twitter.com/1/statuses/update.xml";

            //Dictionary<string, string> requestParams = new Dictionary<string, string>();
            //requestParams.Add("status", message);
            //requestParams.Add("include_entities", "true");

            //TwitterRequestData requestData = CreateRequestData(METHOD, URL, requestParams);

            //HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(requestData.url);
            //request.Headers.Add(HttpRequestHeader.Authorization, requestData.auth);
            //request.Method = requestData.method;

            //if (request.Method == METHOD_POST)
            //{
            //    var s = new StreamWriter(request.GetRequestStream());
            //    s.Write(requestData.post);
            //    s.Close();
            //}

            //HttpWebResponse response = null;
            //try
            //{
            //    using (response = (HttpWebResponse)request.GetResponse())
            //    {
            //    }
            //}
            //catch (WebException we)
            //{
            //    HttpWebResponse res = (HttpWebResponse)we.Response;
            //    string result = new StreamReader(res.GetResponseStream()).ReadToEnd();
            //    ErrorMessage = result;
            //    throw we;
            //}

        }

        public void PostDirectMessage(string user, string message)
        {
            const string METHOD = METHOD_POST;
            const string URL = "https://api.twitter.com/1/direct_messages/new.xml";

            Dictionary<string, string> requestParams = MakeParams();
            requestParams.Add("text", UrlEncodeUpper(message));
            requestParams.Add("screen_name", UrlEncodeUpper(user));

            MakeUrl(METHOD, URL, requestParams);

            string auth = MakeAuth(METHOD, URL, requestParams);

            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(URL);
            request.Headers.Add(HttpRequestHeader.Authorization, auth);

            request.Method = METHOD;

            //System.Diagnostics.Debug.WriteLine("auth:" + auth);

            var s = new StreamWriter(request.GetRequestStream());
            //s.Write("text=" + UrlEncodeUpper(message));
            s.Write("text=" + UrlEncodeUpper(message) + "&" + "screen_name=" + UrlEncodeUpper(user));
            s.Close();

            HttpWebResponse response = null;
            try
            {
                using (response = (HttpWebResponse)request.GetResponse())
                {
                }
            }
            catch (WebException we)
            {
                HttpWebResponse res = (HttpWebResponse)we.Response;
                string result = new StreamReader(res.GetResponseStream()).ReadToEnd();
                ErrorMessage = result;
                throw we;
            }

        }


        protected string MakeAuth(string method, string url, Dictionary<string, string> requestParams)
        {
            //MakeUrl(HTTP_GET, url, requestParams);

            List<string> tmplist = new List<string>();
            string[] keys = GetKeysSorted(requestParams.Keys);
            foreach (var key in keys)
            {
                tmplist.Add(key + "=\"" + UrlEncodeUpper(requestParams[key]) + "\"");
            }

            //List<string> tmplist = new List<string>();
            //foreach (var item in requestParams)
            //{
            //    tmplist.Add(item.Key + "=\"" + UrlEncodeUpper(item.Value) + "\"");
            //}

            string auth = "OAuth " + String.Join(",", tmplist.ToArray());
            return auth;
        }

        protected string MakeAuth2(Dictionary<string, string> requestParams)
        {
            List<string> tmplist = new List<string>();
            string[] keys = GetKeysSorted(requestParams.Keys);
            foreach (var key in keys)
            {
                tmplist.Add(key + "=\"" + UrlEncodeUpper(requestParams[key]) + "\"");
            }

            string auth = "OAuth " + String.Join(",", tmplist.ToArray());
            return auth;
        }

        public UserInfo GetAccountVerifyCredentials()
        {
            const string METHOD = METHOD_GET;
            const string URL = "https://api.twitter.com/1/account/verify_credentials.xml";

            Dictionary<string, string> requestParams = MakeParams();

            MakeUrl(METHOD, URL, requestParams);
            string auth = MakeAuth(METHOD, URL, requestParams);

            string url = URL;
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            request.Headers.Add(HttpRequestHeader.Authorization, auth);
            request.Method = METHOD;

            //            System.Diagnostics.Debug.WriteLine("auth:" + auth);

            HttpWebResponse response = null;
            try
            {
                using (response = (HttpWebResponse)request.GetResponse())
                {
                    XmlDocument doc = new XmlDocument();
                    doc.Load(response.GetResponseStream());

                    //                    XmlNode node = doc.FirstChild;
                    XmlNode node = doc.SelectSingleNode("//user");
                    UserInfo info = new UserInfo();
                    info.id = long.Parse(node["id"].InnerText);
                    info.name = node["name"].InnerText;
                    info.screen_name = node["screen_name"].InnerText;
                    info.url = node["url"].InnerText;
                    CurrentUser = info;
                    return info;
                }
            }
            catch (WebException we)
            {
                HttpWebResponse res = (HttpWebResponse)we.Response;
                string result = new StreamReader(res.GetResponseStream()).ReadToEnd();
                ErrorMessage = result;
                throw we;
            }
        }

        public UserInfo GetUsersShow(long user_id)
        {
            const string METHOD = METHOD_GET;
            const string URL = "https://api.twitter.com/1/users/show.xml";
            //users/show

            Dictionary<string, string> requestParams = MakeParams();
            requestParams.Add("user_id", UrlEncodeUpper(user_id.ToString()));
            //            requestParams.Add("include_entities", "true");

            MakeUrl(METHOD, URL, requestParams);
            string auth = MakeAuth(METHOD, URL, requestParams);

            string url = URL + "?user_id=" + UrlEncodeUpper(user_id.ToString());
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            request.Headers.Add(HttpRequestHeader.Authorization, auth);
            request.Method = METHOD;

            //System.Diagnostics.Debug.WriteLine("auth:" + auth);

            HttpWebResponse response = null;
            try
            {
                using (response = (HttpWebResponse)request.GetResponse())
                {
                    //                    List<StatusItem> list = new List<StatusItem>();
                    XmlDocument doc = new XmlDocument();
                    doc.Load(response.GetResponseStream());
                    XmlNode node = doc.SelectSingleNode("//user");
                    UserInfo info = new UserInfo();
                    info.id = long.Parse(node["id"].InnerText);
                    info.name = node["name"].InnerText;
                    info.screen_name = node["screen_name"].InnerText;
                    info.url = node["url"].InnerText;
                    return info;
                }
            }
            catch (WebException we)
            {
                HttpWebResponse res = (HttpWebResponse)we.Response;
                string result = new StreamReader(res.GetResponseStream()).ReadToEnd();
                ErrorMessage = result;
                throw we;
            }
        }

        public class UserStreamUserEventArgs : EventArgs
        {
            public string user;
            public string message;
        }

        public delegate void UserStreamReadEvent(object sender, UserStreamUserEventArgs e);
        public event UserStreamReadEvent OnUserStreamRead;

        protected void UserStreamUserThread()
        {
            const string METHOD = METHOD_GET;
            const string URL = "https://userstream.twitter.com/2/user.json";

            Dictionary<string, string> requestParams = MakeParams();

            MakeUrl(METHOD, URL, requestParams);
            string auth = MakeAuth(METHOD, URL, requestParams);

            string url = URL;
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
            request.Headers.Add(HttpRequestHeader.Authorization, auth);
            request.Method = METHOD;

            //System.Diagnostics.Debug.WriteLine("auth:" + auth);

            byte[] pst = new byte[8192];
            //            using (var stream = request.GetRequestStream())
            {
                //stream.Write(pst, 0, pst.Length);
                using (var res = request.GetResponse())
                {
                    using (StreamReader reader = new StreamReader(res.GetResponseStream()))
                    {
                        while (true)
                        {
                            while (!reader.EndOfStream)
                            {
                                var s = reader.ReadLine();
                                System.Diagnostics.Debug.WriteLine("str:" + s);
                                UserStreamUserEventArgs ev = new UserStreamUserEventArgs();
                                ev.message = s;
                                OnUserStreamRead.Invoke(this, ev);
                            }
                        }
                    }
                }
            }

        }

        volatile Thread UserStreamThread;
        public void StartUserStreamUser()
        {
            UserStreamThread = new Thread(UserStreamUserThread);
            UserStreamThread.Start();
        }

        public void EndUserStreamUser()
        {
            if (UserStreamThread != null)
            {
                UserStreamThread.Abort();
            }
        }

    }
}
